package org.geektimes.rest.client;

import javax.ws.rs.HttpMethod;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.client.InvocationCallback;
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.geektimes.rest.core.DefaultResponse;

/**
 * Http Post Invocation.
 * <br> @author leo
 * <br> @date 2021/7/21
 * <br> @since 1.0.0
 */
public class HttpPostInvocation implements Invocation {

    private final URI uri;

    private Entity<?> entity;
    MultivaluedMap<String, Object> headers;

    public HttpPostInvocation(URI build, MultivaluedMap<String, Object> headers, Entity<?> entity) {
        this.uri = build;
        this.headers = headers;
        this.entity = entity;
    }

    @Override
    public Invocation property(String name, Object value) {
        return this;
    }

    @Override
    public Response invoke() {
        HttpURLConnection connection;
        try {
            connection = (HttpURLConnection) uri.toURL().openConnection();
            connection.setRequestMethod(HttpMethod.POST);
            setRequestHeaders(connection);
            setBody(connection);
            int statusCode = connection.getResponseCode();
            DefaultResponse response = new DefaultResponse();
            response.setConnection(connection);
            response.setStatus(statusCode);
            return response;
        } catch (IOException e) {
           throw new RuntimeException(e);
        }
    }

    /**
     * 功能: 设置 request body 如果存在.
     * <br> 作者: leo
     * <br>创建日期:2021/7/21
     * <br>修改者: mender
     * <br>修改日期: modify date
     * @param connection {@link HttpURLConnection}
     *
    */
    private void setBody(HttpURLConnection connection){
        if (this.entity == null) {
            return;
        }

        if (this.entity.getMediaType() == MediaType.APPLICATION_JSON_TYPE) {
            connection.setDoOutput(true);
            connection.setDoInput(true);
            try(OutputStream os =connection.getOutputStream()){
                //pojo 转 json
                ObjectMapper objectMapper = new ObjectMapper();
                String body = objectMapper.writeValueAsString(this.entity.getEntity());
                os.write(body.getBytes());
                os.flush();
                return;
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        throw new UnsupportedOperationException(String.format("仅支持 mediaType:%s",
                MediaType.APPLICATION_JSON));
    }

    /**
     * 功能: 设置 http headers.
     * <br> 作者: leo
     * <br>创建日期:2021/7/21
     * <br>修改者: mender
     * <br>修改日期: modify date
     * @param connection {@link HttpURLConnection}
     *
    */
    private void setRequestHeaders(HttpURLConnection connection) {
        for (Map.Entry<String, List<Object>> entry : headers.entrySet()) {
            String headerName = entry.getKey();
            for (Object headerValue : entry.getValue()) {
                connection.setRequestProperty(headerName, headerValue.toString());
            }
        }
    }

    @Override
    public <T> T invoke(Class<T> responseType) {
        return invoke().readEntity(responseType);
    }

    @Override
    public <T> T invoke(GenericType<T> responseType) {
        Response response = invoke();
        return response.readEntity(responseType);
    }

    @Override
    public Future<Response> submit() {
        return null;
    }

    @Override
    public <T> Future<T> submit(Class<T> responseType) {
        return null;
    }

    @Override
    public <T> Future<T> submit(GenericType<T> responseType) {
        return null;
    }

    @Override
    public <T> Future<T> submit(InvocationCallback<T> callback) {
        return null;
    }
}
